package network

import (
	"leaf/core/log"
	"leaf/core/tool"
	"net"
	"sync"
	"sync/atomic"
	"time"
)

var (
	Default_MaxConnNum = 1000
)

type TcpServer struct {
	Addr       string
	NewAgent   func(conn Conn) Agent
	MaxConnNum int
	ln         net.Listener
	conns      map[net.Conn]*TcpConn
	mutexConns sync.Mutex
	wgLn       sync.WaitGroup
	wgConns    sync.WaitGroup
	msgParser  *MsgParser
	closeFlag  int32

	OnAccept func(conn Conn)
}

func NewTcpServer(addr string, maxConnNum int, createAgent func(conn Conn) Agent) *TcpServer {
	s := new(TcpServer)
	s.Addr = addr
	s.MaxConnNum = maxConnNum
	if s.MaxConnNum <= 0 {
		s.MaxConnNum = Default_MaxConnNum
	}
	s.NewAgent = createAgent
	if s.OnAccept == nil {

	}
	s.conns = make(map[net.Conn]*TcpConn, s.MaxConnNum)
	return s
}

func (server *TcpServer) Start(asyn bool) {
	ln, err := net.Listen("tcp", server.Addr)
	if err != nil {
		log.Fatal("%v", err)
	}

	server.ln = ln
	if server.msgParser == nil {
		server.msgParser = DefaultMsgParser
	}

	accept_func := func() {
		server.wgLn.Add(1)
		defer func() {
			server.wgLn.Done()
			server.Close()
		}()

		var tempDelay time.Duration
		for tool.IsRuning() {
			conn, err := server.ln.Accept()
			if err != nil {
				if ne, ok := err.(net.Error); ok && ne.Temporary() {
					if tempDelay == 0 {
						tempDelay = 5 * time.Millisecond
					} else {
						tempDelay *= 2
					}
					if max := 1 * time.Second; tempDelay > max {
						tempDelay = max
					}
					log.Release("accept error: %v; retrying in %v", err, tempDelay)
					time.Sleep(tempDelay)
					continue
				}
				return
			}
			tempDelay = 0

			server.mutexConns.Lock()
			connsLen := len(server.conns)
			server.mutexConns.Unlock()
			if connsLen >= server.MaxConnNum {
				conn.Close()
				log.Debug("too many connections")
				continue
			}

			tcpConn := newTcpAcceptConn(conn, server.msgParser, server)
			if tcpConn == nil {
				conn.Close()
				log.Debug("new tcp accept conn fail")
				continue
			}
			server.mutexConns.Lock()
			server.conns[conn] = tcpConn
			server.mutexConns.Unlock()

			tcpConn.agent = server.NewAgent(tcpConn)
			server.wgConns.Add(1)
			tcpConn.Start()
			if server.OnAccept != nil {
				server.OnAccept(tcpConn)
			}
		}
	}
	if !asyn {
		accept_func()
	} else {
		tool.Go(accept_func)
	}
}

func (server *TcpServer) onCloseConn(tcpConn *TcpConn) {
	server.wgConns.Add(-1)
	server.mutexConns.Lock()
	delete(server.conns, tcpConn.conn)
	server.mutexConns.Unlock()
	tcpConn.Destroy()
}

func (server *TcpServer) Close() {
	if !atomic.CompareAndSwapInt32(&server.closeFlag, 0, 1) {
		return
	}
	if server.ln != nil {
		server.ln.Close()
		server.ln = nil
	}
	//等待监听线程退出
	server.wgLn.Wait()

	var conns []*TcpConn
	server.mutexConns.Lock()
	for _, tcpConn := range server.conns {
		conns = append(conns, tcpConn)
	}
	server.mutexConns.Unlock()

	//关闭所用连接
	for _, tcpConn := range conns {
		tcpConn.Close()
	}
	//等待所有连接线程返回
	server.wgConns.Wait()
	//销毁连接资源
	for _, tcpConn := range conns {
		tcpConn.Destroy()
	}
}
